home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / cvs-1_3.lha / cvs-1.3 / src / patch.c < prev    next >
C/C++ Source or Header  |  1992-04-09  |  13KB  |  524 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.3 kit.
  7.  * 
  8.  * Patch
  9.  * 
  10.  * Create a Larry Wall format "patch" file between a previous release and the
  11.  * current head of a module, or between two releases.  Can specify the
  12.  * release as either a date or a revision number.
  13.  */
  14.  
  15. #include "cvs.h"
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "@(#)patch.c 1.50 92/04/10";
  19.  
  20. #endif
  21.  
  22. #if __STDC__
  23. static SIGTYPE patch_cleanup (void);
  24. static Dtype patch_dirproc (char *dir, char *repos, char *update_dir);
  25. static int patch_fileproc (char *file, char *update_dir, char *repository,
  26.                List * entries, List * srcfiles);
  27. static int patch_proc (int *pargc, char *argv[], char *xwhere,
  28.                char *mwhere, char *mfile, int shorten,
  29.                int local_specified, char *mname, char *msg);
  30. #else
  31. static int patch_proc ();
  32. static int patch_fileproc ();
  33. static Dtype patch_dirproc ();
  34. static SIGTYPE patch_cleanup ();
  35. #endif                /* __STDC__ */
  36.  
  37. static int force_tag_match = 1;
  38. static int patch_short = 0;
  39. static int toptwo_diffs = 0;
  40. static int local = 0;
  41. static char *options = NULL;
  42. static char *rev1 = NULL;
  43. static char *rev2 = NULL;
  44. static char *date1 = NULL;
  45. static char *date2 = NULL;
  46. static char tmpfile1[L_tmpnam+1], tmpfile2[L_tmpnam+1], tmpfile3[L_tmpnam+1];
  47. static int unidiff = 0;
  48.  
  49. static char *patch_usage[] =
  50. {
  51.     "Usage: %s %s [-Qflq] [-c|-u] [-s|-t] [-V %%d]\n",
  52.     "    -r rev|-D date [-r rev2 | -D date2] modules...\n",
  53.     "\t-Q\tReally quiet.\n",
  54.     "\t-f\tForce a head revision match if tag/date not found.\n",
  55.     "\t-l\tLocal directory only, not recursive\n",
  56.     "\t-c\tContext diffs (default)\n",
  57.     "\t-u\tUnidiff format.\n",
  58.     "\t-s\tShort patch - one liner per file.\n",
  59.     "\t-t\tTop two diffs - last change made to the file.\n",
  60.     "\t-D date\tDate.\n",
  61.     "\t-r rev\tRevision - symbolic or numeric.\n",
  62.     "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
  63.     NULL
  64. };
  65.  
  66. int
  67. patch (argc, argv)
  68.     int argc;
  69.     char *argv[];
  70. {
  71.     register int i;
  72.     int c;
  73.     int err = 0;
  74.     DBM *db;
  75.  
  76.     if (argc == -1)
  77.     usage (patch_usage);
  78.  
  79.     optind = 1;
  80.     while ((c = gnu_getopt (argc, argv, "V:k:cuftsQqlRD:r:")) != -1)
  81.     {
  82.     switch (c)
  83.     {
  84.         case 'Q':
  85.         really_quiet = 1;
  86.         /* FALL THROUGH */
  87.         case 'q':
  88.         quiet = 1;
  89.         break;
  90.         case 'f':
  91.         force_tag_match = 0;
  92.         break;
  93.         case 'l':
  94.         local = 1;
  95.         break;
  96.         case 'R':
  97.         local = 0;
  98.         break;
  99.         case 't':
  100.         toptwo_diffs = 1;
  101.         break;
  102.         case 's':
  103.         patch_short = 1;
  104.         break;
  105.         case 'D':
  106.         if (rev2 != NULL || date2 != NULL)
  107.             error (1, 0,
  108.                "no more than two revisions/dates can be specified");
  109.         if (rev1 != NULL || date1 != NULL)
  110.             date2 = Make_Date (optarg);
  111.         else
  112.             date1 = Make_Date (optarg);
  113.         break;
  114.         case 'r':
  115.         if (rev2 != NULL || date2 != NULL)
  116.             error (1, 0,
  117.                "no more than two revisions/dates can be specified");
  118.         if (rev1 != NULL || date1 != NULL)
  119.             rev2 = optarg;
  120.         else
  121.             rev1 = optarg;
  122.         break;
  123.         case 'k':
  124.         if (options)
  125.             free (options);
  126.         options = RCS_check_kflag (optarg);
  127.         break;
  128.         case 'V':
  129.         if (atoi (optarg) <= 0)
  130.             error (1, 0, "must specify a version number to -V");
  131.         if (options)
  132.             free (options);
  133.         options = xmalloc (strlen (optarg) + 1 + 2);    /* for the -V */
  134.         (void) sprintf (options, "-V%s", optarg);
  135.         break;
  136.         case 'u':
  137.         unidiff = 1;        /* Unidiff */
  138.         break;
  139.         case 'c':            /* Context diff */
  140.         unidiff = 0;
  141.         break;
  142.         case '?':
  143.         default:
  144.         usage (patch_usage);
  145.         break;
  146.     }
  147.     }
  148.     argc -= optind;
  149.     argv += optind;
  150.  
  151.     /* Sanity checks */
  152.     if (argc < 1)
  153.     usage (patch_usage);
  154.  
  155.     if (toptwo_diffs && patch_short)
  156.     error (1, 0, "-t and -s options are mutually exclusive");
  157.     if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
  158.              rev1 != NULL || rev2 != NULL))
  159.     error (1, 0, "must not specify revisions/dates with -t option!");
  160.  
  161.     if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
  162.               rev1 == NULL && rev2 == NULL))
  163.     error (1, 0, "must specify at least one revision/date!");
  164.     if (date1 != NULL && date2 != NULL)
  165.     if (RCS_datecmp (date1, date2) >= 0)
  166.         error (1, 0, "second date must come after first date!");
  167.  
  168.     /* if options is NULL, make it a NULL string */
  169.     if (options == NULL)
  170.     options = xstrdup ("");
  171.  
  172.     /* clean up if we get a signal */
  173.     (void) SIG_register (SIGHUP, patch_cleanup);
  174.     (void) SIG_register (SIGINT, patch_cleanup);
  175.     (void) SIG_register (SIGQUIT, patch_cleanup);
  176.     (void) SIG_register (SIGPIPE, patch_cleanup);
  177.     (void) SIG_register (SIGTERM, patch_cleanup);
  178.  
  179.     db = open_module ();
  180.     for (i = 0; i < argc; i++)
  181.     err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
  182.               (char *) NULL, 0, 0, 0, (char *) NULL);
  183.     close_module (db);
  184.     free (options);
  185.     patch_cleanup ();
  186.     return (err);
  187. }
  188.  
  189. /*
  190.  * callback proc for doing the real work of patching
  191.  */
  192. /* ARGSUSED */
  193. static char where[PATH_MAX];
  194. static int
  195. patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
  196.         mname, msg)
  197.     int *pargc;
  198.     char *argv[];
  199.     char *xwhere;
  200.     char *mwhere;
  201.     char *mfile;
  202.     int shorten;
  203.     int local_specified;
  204.     char *mname;
  205.     char *msg;
  206. {
  207.     int err = 0;
  208.     int which;
  209.     char repository[PATH_MAX];
  210.  
  211.     (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
  212.     (void) strcpy (where, argv[0]);
  213.  
  214.     /* if mfile isn't null, we need to set up to do only part of the module */
  215.     if (mfile != NULL)
  216.     {
  217.     char *cp;
  218.     char path[PATH_MAX];
  219.  
  220.     /* if the portion of the module is a path, put the dir part on repos */
  221.     if ((cp = rindex (mfile, '/')) != NULL)
  222.     {
  223.         *cp = '\0';
  224.         (void) strcat (repository, "/");
  225.         (void) strcat (repository, mfile);
  226.         (void) strcat (where, "/");
  227.         (void) strcat (where, mfile);
  228.         mfile = cp + 1;
  229.     }
  230.  
  231.     /* take care of the rest */
  232.     (void) sprintf (path, "%s/%s", repository, mfile);
  233.     if (isdir (path))
  234.     {
  235.         /* directory means repository gets the dir tacked on */
  236.         (void) strcpy (repository, path);
  237.         (void) strcat (where, "/");
  238.         (void) strcat (where, mfile);
  239.     }
  240.     else
  241.     {
  242.         int i;
  243.  
  244.         /* a file means muck argv */
  245.         for (i = 1; i < *pargc; i++)
  246.         free (argv[i]);
  247.         argv[1] = xstrdup (mfile);
  248.         (*pargc) = 2;
  249.     }
  250.     }
  251.  
  252.     /* cd to the starting repository */
  253.     if (chdir (repository) < 0)
  254.     {
  255.     error (0, errno, "cannot chdir to %s", repository);
  256.     return (1);
  257.     }
  258.  
  259.     if (force_tag_match)
  260.     which = W_REPOS | W_ATTIC;
  261.     else
  262.     which = W_REPOS;
  263.  
  264.     /* start the recursion processor */
  265.     err = start_recursion (patch_fileproc, (int (*) ()) NULL, patch_dirproc,
  266.                (int (*) ()) NULL, *pargc - 1, argv + 1, local,
  267.                which, 0, 1, where, 1);
  268.  
  269.     return (err);
  270. }
  271.  
  272. /*
  273.  * Called to examine a particular RCS file, as appropriate with the options
  274.  * that were set above.
  275.  */
  276. /* ARGSUSED */
  277. static int
  278. patch_fileproc (file, update_dir, repository, entries, srcfiles)
  279.     char *file;
  280.     char *update_dir;
  281.     char *repository;
  282.     List *entries;
  283.     List *srcfiles;
  284. {
  285.     char *vers_tag, *vers_head;
  286.     char rcsspace[PATH_MAX];
  287.     char *rcs = rcsspace;
  288.     Node *p;
  289.     RCSNode *rcsfile;
  290.     FILE *fp1, *fp2, *fp3;
  291.     int ret = 0;
  292.     int isattic = 0;
  293.     int retcode = 0;
  294.     char file1[PATH_MAX], file2[PATH_MAX], strippath[PATH_MAX];
  295.     char line1[MAXLINELEN], line2[MAXLINELEN];
  296.     char *cp1, *cp2, *commap;
  297.     FILE *fp;
  298.  
  299.  
  300.     /* find the parsed rcs file */
  301.     p = findnode (srcfiles, file);
  302.     if (p == NULL)
  303.     return (1);
  304.     rcsfile = (RCSNode *) p->data;
  305.     if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
  306.     isattic = 1;
  307.  
  308.     (void) sprintf (rcs, "%s%s", file, RCSEXT);
  309.  
  310.     /* if vers_head is NULL, may have been removed from the release */
  311.     if (isattic && rev2 == NULL && date2 == NULL)
  312.     vers_head = NULL;
  313.     else
  314.     vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match);
  315.  
  316.     if (toptwo_diffs)
  317.     {
  318.     if (vers_head == NULL)
  319.         return (1);
  320.  
  321.     if (!date1)
  322.         date1 = xmalloc (50);    /* plenty big :-) */
  323.     *date1 = '\0';
  324.     if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == -1)
  325.     {
  326.         if (!really_quiet)
  327.         error (0, 0, "cannot find date in rcs file %s revision %s",
  328.                rcs, vers_head);
  329.         return (1);
  330.     }
  331.     }
  332.     vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match);
  333.  
  334.     if (vers_tag == NULL && (vers_head == NULL || isattic))
  335.     return (0);            /* nothing known about specified revs */
  336.  
  337.     if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0)
  338.     return (0);            /* not changed between releases */
  339.  
  340.     if (patch_short)
  341.     {
  342.     (void) printf ("File ");
  343.     if (vers_tag == NULL)
  344.         (void) printf ("%s is new; current revision %s\n", rcs, vers_head);
  345.     else if (vers_head == NULL)
  346.         (void) printf ("%s is removed; not included in release %s\n",
  347.                rcs, rev2 ? rev2 : date2);
  348.     else
  349.         (void) printf ("%s changed from revision %s to %s\n",
  350.                rcs, vers_tag, vers_head);
  351.     return (0);
  352.     }
  353.     if ((fp1 = fopen (tmpnam (tmpfile1), "w+")) != NULL)
  354.     (void) fclose (fp1);
  355.     if ((fp2 = fopen (tmpnam (tmpfile2), "w+")) != NULL)
  356.     (void) fclose (fp2);
  357.     if ((fp3 = fopen (tmpnam (tmpfile3), "w+")) != NULL)
  358.     (void) fclose (fp3);
  359.     if (fp1 == NULL || fp2 == NULL || fp3 == NULL)
  360.     {
  361.     error (0, 0, "cannot create temporary files");
  362.     ret = 1;
  363.     goto out;
  364.     }
  365.     if (vers_tag != NULL)
  366.     {
  367.     run_setup ("%s%s %s -p -q -r%s", Rcsbin, RCS_CO, options, vers_tag);
  368.     run_arg (rcsfile->path);
  369.     if ((retcode = run_exec (RUN_TTY, tmpfile1, RUN_TTY, RUN_NORMAL)) != 0)
  370.     {
  371.         if (!really_quiet)
  372.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  373.                "co of revision %s in %s failed", vers_tag, rcs);
  374.         ret = 1;
  375.         goto out;
  376.     }
  377.     }
  378.     else if (toptwo_diffs)
  379.     {
  380.     ret = 1;
  381.     goto out;
  382.     }
  383.     if (vers_head != NULL)
  384.     {
  385.     run_setup ("%s%s %s -p -q -r%s", Rcsbin, RCS_CO, options, vers_head);
  386.     run_arg (rcsfile->path);
  387.     if ((retcode = run_exec (RUN_TTY, tmpfile2, RUN_TTY, RUN_NORMAL)) != 0)
  388.     {
  389.         if (!really_quiet)
  390.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  391.                "co of revision %s in %s failed", vers_head, rcs);
  392.         ret = 1;
  393.         goto out;
  394.     }
  395.     }
  396.     run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c');
  397.     run_arg (tmpfile1);
  398.     run_arg (tmpfile2);
  399.     switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_NORMAL))
  400.     {
  401.     case -1:            /* fork/wait failure */
  402.         error (1, errno, "fork for diff failed on %s", rcs);
  403.         break;
  404.     case 0:                /* nothing to do */
  405.         break;
  406.     case 1:
  407.         /*
  408.          * The two revisions are really different, so read the first two
  409.          * lines of the diff output file, and munge them to include more
  410.          * reasonable file names that "patch" will understand.
  411.          */
  412.         fp = open_file (tmpfile3, "r");
  413.         if (fgets (line1, sizeof (line1), fp) == NULL ||
  414.         fgets (line2, sizeof (line2), fp) == NULL)
  415.         {
  416.         error (0, errno, "failed to read diff file header %s for %s",
  417.                tmpfile3, rcs);
  418.         ret = 1;
  419.         (void) fclose (fp);
  420.         goto out;
  421.         }
  422.         if (!unidiff)
  423.         {
  424.         if (strncmp (line1, "*** ", 4) != 0 ||
  425.             strncmp (line2, "--- ", 4) != 0 ||
  426.             (cp1 = index (line1, '\t')) == NULL ||
  427.             (cp2 = index (line2, '\t')) == NULL)
  428.         {
  429.             error (0, 0, "invalid diff header for %s", rcs);
  430.             ret = 1;
  431.             (void) fclose (fp);
  432.             goto out;
  433.         }
  434.         }
  435.         else
  436.         {
  437.         if (strncmp (line1, "--- ", 4) != 0 ||
  438.             strncmp (line2, "+++ ", 4) != 0 ||
  439.             (cp1 = index (line1, '\t')) == NULL ||
  440.             (cp2 = index (line2, '\t')) == NULL)
  441.         {
  442.             error (0, 0, "invalid unidiff header for %s", rcs);
  443.             ret = 1;
  444.             (void) fclose (fp);
  445.             goto out;
  446.         }
  447.         }
  448.         if (CVSroot != NULL)
  449.         (void) sprintf (strippath, "%s/", CVSroot);
  450.         else
  451.         (void) strcpy (strippath, REPOS_STRIP);
  452.         if (strncmp (rcs, strippath, strlen (strippath)) == 0)
  453.         rcs += strlen (strippath);
  454.         commap = rindex (rcs, ',');
  455.         *commap = '\0';
  456.         if (vers_tag != NULL)
  457.         {
  458.         (void) sprintf (file1, "%s%s%s:%s", update_dir,
  459.                 update_dir[0] ? "/" : "", rcs, vers_tag);
  460.         }
  461.         else
  462.         {
  463.         (void) strcpy (file1, DEVNULL);
  464.         }
  465.         (void) sprintf (file2, "%s%s%s:%s", update_dir,
  466.                 update_dir[0] ? "/" : "", rcs,
  467.                 vers_head ? vers_head : "removed");
  468.         if (unidiff)
  469.         {
  470.         (void) printf ("diff -u %s %s\n", file1, file2);
  471.         (void) printf ("--- %s%s+++ ", file1, cp1);
  472.         }
  473.         else
  474.         {
  475.         (void) printf ("diff -c %s %s\n", file1, file2);
  476.         (void) printf ("*** %s%s--- ", file1, cp1);
  477.         }
  478.  
  479.         if (update_dir[0] != '\0')
  480.         (void) printf ("%s/", update_dir);
  481.         (void) printf ("%s%s", rcs, cp2);
  482.         while (fgets (line1, sizeof (line1), fp) != NULL)
  483.         (void) printf ("%s", line1);
  484.         (void) fclose (fp);
  485.         break;
  486.     default:
  487.         error (0, 0, "diff failed for %s", rcs);
  488.     }
  489.   out:
  490.     (void) unlink_file (tmpfile1);
  491.     (void) unlink_file (tmpfile2);
  492.     (void) unlink_file (tmpfile3);
  493.     return (ret);
  494. }
  495.  
  496. /*
  497.  * Print a warm fuzzy message
  498.  */
  499. /* ARGSUSED */
  500. static Dtype
  501. patch_dirproc (dir, repos, update_dir)
  502.     char *dir;
  503.     char *repos;
  504.     char *update_dir;
  505. {
  506.     if (!quiet)
  507.     error (0, 0, "Diffing %s", update_dir);
  508.     return (R_PROCESS);
  509. }
  510.  
  511. /*
  512.  * Clean up temporary files
  513.  */
  514. static SIGTYPE
  515. patch_cleanup ()
  516. {
  517.     if (tmpfile1[0] != '\0')
  518.     (void) unlink_file (tmpfile1);
  519.     if (tmpfile2[0] != '\0')
  520.     (void) unlink_file (tmpfile2);
  521.     if (tmpfile3[0] != '\0')
  522.     (void) unlink_file (tmpfile3);
  523. }
  524.